import os
from datetime import datetime

from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.utils.decorators import method_decorator
from django.views import View

from django_study.settings import MEDIA_ROOT
from .forms import ArticleForm
from .models import Classes, UserDetail, Article


# Create your views here.
# data = {
#     'python': [{'title': 'Python开篇：撬动未来的万能钥匙 —— 从入门到架构的全链路指南',
#                 'desc': '在技术的星空中，Python 是那颗永不陨落的超新星——它用简洁的语法点燃创造之火，以庞大的生态铺就革新之路。无论你身处哪个领域，这把钥匙正在打开下一个时代的大门。2024 年 TIOBE 指数显示，Python 连续五年稳居编程语言榜首，其开发者社区规模同比增长 42%，成为全球技术变革的核心驱动力。',
#                 'detail': 'https://blog.csdn.net/weixin_43151418/article/details/148957574',
#                 'img': 'https://i-blog.csdnimg.cn/direct/aa24c69ac70840a5a216537347cf2f67.png',
#                 'created_time': datetime.now()
#                 },
#                {'title': '【Python】【数据分析】Python 数据分析与可视化：全面指南',
#                 'desc': '本文演示了如何使用 Python 进行数据分析与可视化。关键步骤包括数据处理、分析和可视化。使用pandas进行数据清洗与分析，使用matplotlib和seaborn进行可视化，展示了不同的图表和分析结果。在实际应用中，数据分析与可视化是一个迭代的过程，您可以根据需求不断调整和优化。希望这篇博客能为您提供一个清晰的入门指南，助力您在数据分析与可视化的旅程中不断前行！如果有任何问题或建议，欢迎在评论区讨论！',
#                 'detail': 'https://blog.csdn.net/Stromboli/article/details/142552381?spm=1000.2115.3001.10526&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-142552381-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-142552381-null-null.nonecase',
#                 'img': 'https://i-blog.csdnimg.cn/direct/887f779f1099467fafd422a5fc391ac3.png',
#                 'created_time': datetime(2024, 11, 1, 21, 45)
#                 },
#                {'title': '机器学习：数据清洗与预处理 | Python',
#                 'desc': '我们不论在学习机器学习还是数据分析中，都会涉及很多数据。但原数据不可避免有很多杂志，为了确保结果的准确性，我们需要首先进行数据清洗和预处理。本文聚焦机器学习中的数据清洗与预处理。先阐述其重要性，如同为数据“大扫除”，能提升数据质量、保障结论可靠。接着分八个步骤详细讲解，从环境准备与库导入，到数据加载、初探，再到缺失值、重复值、异常值处理，以及数据类型转换和标准化/归一化，每个步骤都有代码示例和函数解释。最后以鸢尾花数据集为例实践，经各环节处理后保存清洗数据。',
#                 'detail': 'https://blog.csdn.net/2401_87533975/article/details/149427162?spm=1000.2115.3001.10526&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-4-149427162-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-4-149427162-null-null.nonecase',
#                 'img': 'https://i-blog.csdnimg.cn/direct/6573187aae9f46c9bda8c4d2d253d04b.jpeg',
#                 'created_time': 'datetime.now()'
#                 },
#                {'title': '【Python】Python 数据可视化：Matplotlib 绘图基础',
#                 'desc': 'Matplotlib 是 Python 的一个绘图库，用于创建静态、动态和交互式的数据可视化。本文将介绍 Matplotlib 的基本用法，包括创建图表、设置标题、标签、颜色、大小、样式等。',
#                 'detail': 'https://blog.csdn.net/weixin_43151418/article/details/148957574',
#                 'img': 'https://i-blog.csdnimg.cn/direct/28b550495f4f4fe3bf050917a92375d2.png',
#                 'created_time': datetime(2024, 11, 1, 21, 45)
#                 }
#                ],
#     'java': [{
#         'title': '飞算JavaAI：赋能Java开发，从“代码工匠”到“软件架构师”的跃迁',
#         'desc': '飞算JavaAI通过智能分析快速生成模块依赖图和重构方案，助力团队高效完成新服务集成。该工具基于飞算科技200余项专利，提供遗留系统分析、企业级规范引擎、模块化引导生成等核心能力，支持一键生成完整工程代码，彻底提升开发效率。实战案例展示了如何将混乱的订单处理类解耦为符合单一职责原则的模块化结构，验证了其作为&amp;quot;架构师级AI伙伴&amp;quot;的价值。',
#         'detail': 'https://blog.csdn.net/SDFsoul/article/details/149068242?spm=1000.2115.3001.10526&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-5-149068242-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-5-149068242-null-null.nonecase',
#         'img': 'https://i-blog.csdnimg.cn/direct/73cd98dac1b441a984cb6f30e21c7d58.png',
#         'created_time': datetime(2024, 11, 1, 21, 45)
#     },
#         {
#             'title': '《5分钟开发订单微服务！飞算JavaAI实战：IDEA插件安装→空指针修复→K8s部署全流程》',
#             'desc': '《飞算JavaAI：40倍提效的智能开发引擎》摘要：本文介绍飞算JavaAI如何通过三大核心能力重塑Java开发流程：1）智能代码生成（5分钟完成订单微服务开发）；2）自动诊断修复（秒级定位NPE等隐患）；3）云原生一键部署（自动生成K8s配置）。实战演示从IDEA插件安装到语音生成代码的全流程，对比显示CRUD开发效率提升40倍，云原生配置效率提升360倍。该工具通过AI生成生产级代码、智能优化SQL索引、自动注入安全校验等特性，实现开发效率与代码质量的双重突破。',
#             'detail': 'https://blog.csdn.net/2201_76027234/article/details/149452481',
#             'img': 'https://i-blog.csdnimg.cn/direct/e04ba79a75a044bfa806264fb234b1b9.png',
#             'created_time': datetime(2023, 11, 1, 21, 45)
#         },
#         {
#             'title': '从深夜加班到高效编程：飞算JavaAI让Java开发焕发新生',
#             'desc': '飞算JavaAI不仅仅是一个代码补全工具，更是一个理解项目需求、支持全流程开发的AI编程伙伴。它能智能分析老项目架构，支持自定义开发规范，提供引导式开发流程，并能一键生成可运行项目。对于被繁琐开发工作困扰的Java程序员来说，飞算JavaAI提供了一条提升效率、减轻负担的新路径，真正让编程体验既高效又充满成就感。',
#             'detail': 'https://blog.csdn.net/weixin_66401877/article/details/149066692?spm=1000.2115.3001.10526&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-4-149066692-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-4-149066692-null-null.nonecase',
#             'img': 'https://i-blog.csdnimg.cn/direct/67c9e9f2f5f24db78e1eaf811f820f14.png',
#             'created_time': datetime(2023, 11, 3, 21, 45)
#         },
#     ],
#     'c++': [{
#         'title': '【C++指南】string（四）：编码',
#         'desc': '本文将深入探讨 C++ 中string所涉及的多种编码规则，包括 ASCII、Unicode、UTF - 8、UTF - 16 和 UTF - 32 等，并着重讲解 UTF - 8 编码以及它在string中灵活存储字符串的机制。',
#         'detail': 'https://blog.csdn.net/2302_78391795/article/details/146516162',
#         'img': 'https://i-blog.csdnimg.cn/direct/d8599612d1c441049ea16ba5a1da1cf8.png',
#         'created_time': datetime.now()
#     },
#     ],
#     'DeepSeek': [],
# }
# user = {
#     'name': 'rose',
#     'age': 18,
#     'sex': '女',
# }


# user = None
def index(request):
    # username = request.session.get('username')
    # 因为换了authentication，所以这里需要修改一下,上面的方法获取不到用户名
    username = request.user.username
    return render(request, 'demo/index.html', context={'class_keys': Classes.objects.all(),
                                                       'username': username})


def test1(request, **kwargs):
    info = kwargs.get('in')
    print(info)
    return HttpResponse('我是测试内容，如果看到，请忽略！')


def nav(request, **kwargs):
    classes = kwargs.get('classes')
    username = request.user.username
    # if classes not in data.keys():
    #     return redirect('Not_Find')
    # print(classes)
    # res = data.get(classes)
    try:
        res = Classes.objects.get(name=classes)
    except Classes.DoesNotExist:
        return redirect('not_found')
        # return redirect('not_found')

    return HttpResponse(render(request, 'demo/nav.html', context={'articles': res.article_set.all(),
                                                                  'now_cls': classes,
                                                                  'class_keys': Classes.objects.all(),
                                                                  'username': username,
                                                                  }))


def not_found(request, **kwargs):
    return HttpResponse('输入的分类不存在，请核实是否输入正确！')


def save_info(request, **kwargs):
    # cls = Classes(name='deepseek')
    # cls.save()
    # res = Classes.objects.get(id=4)
    # res.delete()
    # 创建用户对象,注意这里默认的last_login不能为空，所以这里要设置一个默认值
    # user = User.objects.create_user(username='lisa',
    #                                 password='qwe123',
    #                                 email='lisa@qq.com',
    #                                 last_login=datetime.now(),
    #
    #                                 )
    # user = User(username='rose', last_login=datetime.now())
    # 对密码进行加密
    # user.set_password('qwe123')
    # user.save()
    # 创建用户详情对象, 关联用户对象(用上面的对象)
    # UserDetail.objects.create(age=21, sex=0, user_id=2)
    # 获取用户lisa的年龄
    # user = User.objects.get(username='lisa')
    # age = user.userdetail.age
    # print(age)
    # 获取用户详情里面id为1的信息对应的用户名字
    # userdetail = UserDetail.objects.get(id=1)
    # print(userdetail.user.username)
    # 一对多关系表添加数据
    # 先获取一个python分类对象
    # cls = Classes.objects.get(name='python')
    # Article.objects.create(title='Python开篇：撬动未来的万能钥匙 —— 从入门到架构的全链路指南',
    #                        desc='在技术的星空中，Python 是那颗永不陨落的超新星——它用简洁的语法点燃创造之火，以庞大的生态铺就革新之路。无论你身处哪个领域，这把钥匙正在打开下一个时代的大门。',
    #                        content='在技术的星空中，Python 是那颗永不陨落的超新星——它用简洁的语法点燃创造之火，以庞大的生态铺就革新之路。无论你身处哪个领域，这把钥匙正在打开下一个时代的大门。2024 年 TIOBE 指数显示，Python 连续五年稳居编程语言榜首，其开发者社区规模同比增长 42%，成为全球技术变革的核心驱动力。',
    #                        cover='https://i-blog.csdnimg.cn/direct/aa24c69ac70840a5a216537347cf2f67.png',
    #                        classes=cls,
    #                        user_id=1,
    #                        )
    # 一对多关系表查询数据
    cls = Classes.objects.get(name='python')
    articles = cls.article_set.all()
    for article in articles:
        print(article.title)

    return HttpResponse('成功！')


class AddArticleView(View):
    # 这个是用户发表文章的视图，所以得先通过登录验证以后方可。
    #  添加登录校验的权限 用的是auth系统 不会在session表中查询
    @method_decorator(login_required)
    def get(self, request, **kwargs):
        # 请求获得发表的空表单
        form = ArticleForm()
        # 请求对象里面是用用户对象的
        user = request.user
        username = user.username
        print(username)
        return render(request, 'demo/add_article.html', context={'form': form, 'username': username})

    def post(self, request, **kwargs):
        # 请求获得准备发表文章的文章数据
        # 绑定用户数据表单对象
        # 当需要使用表单时并且需要绑定文件数据 文件数据需要跟普通的表单数据分开处理
        # 文件数据并不会放到POST 而是存放到file参数里面
        form = ArticleForm(request.POST, request.FILES)
        username = request.user.username
        # 用.is_valid()据验证表单数,如果验证通过，则把数据序列化出来，封装到对象里面去
        if form.is_valid():
            # 获取用户对象
            user = request.user
            # 获取表单数据,在cleaned_data对象里面获取-->cleaned_data这个是通过验证的数据封装其里面
            title = form.cleaned_data.get('title')
            desc = form.cleaned_data.get('desc')
            content = form.cleaned_data.get('content')
            cover = form.cleaned_data.get('cover')
            labels = form.cleaned_data.get('labels')
            classes = form.cleaned_data.get('classes')
            # 保存图片， 获取图片的保存路径，这里不会自动保存，要我们写保存代码。
            file_name = f'{datetime.now().strftime("%Y%m%d%H%M%S")}_{cover.name}'
            # 图片地址
            file_path = os.path.join(MEDIA_ROOT, file_name)
            print(file_path)
            with open(file_path, 'wb') as f:
                # 当你的图片文件过大的时候 chunks 上传的文件数据分批次写入
                for chunk in cover.chunks():
                    f.write(chunk)
            # 创建文章对象
            # 注意：cover的值是图片的路径，这个路径是相对于media_root的路径，所以需要拼接上media_root
            article = Article.objects.create(
                title=title,
                desc=desc,
                content=content,
                cover='/static/media/' + file_name,
                user=user,
                classes=classes)
            for item in labels:
                article.labels.add(item)
            return render(request, 'demo/add_article.html', context={'username': username, 'form': form})


class ArticleDetailView(View):
    # 获取文章详情,得1先获取文章，要通id获取，唯一标识
    def get(self, request, id):
        try:
            article = Article.objects.get(id=id)
        except Article.DoesNotExist:
            return redirect('not_found')
        return render(request, 'demo/article_detail.html',
                      context={'article': article, 'labels': article.labels.all()})


class ArticleUserView(View):
    # 添加权限校验
    @method_decorator(login_required)
    def get(self, request, **kwargs):
        # 查询到的该用户发布的所有文章信息
        articles = Article.objects.filter(user=request.user)
        # 自动把文章信息做分页的操作
        #  创建一个分页器对象(每页只显示两篇文章),就是把所有文章按每2篇进行分页，每页拥有2个文章对象
        pg = Paginator(articles, 2)
        try:
            # 获取当前页码数
            page_current = request.GET.get('page')
            # 获取当前的页面数据（文章对象）
            pages = pg.page(page_current)
        except PageNotAnInteger:
            # 如果用户输入的页码数不是数字，就返回第一页
            pages = pg.page(1)
        except EmptyPage:
            pages = pg.page(pg.num_pages)  # 如果用户输入的页码数大于总页码数，就返回最后一页
        return render(request, 'demo/article_user.html', context={'articles': pages,
                                                                  'username': request.user.username,
                                                                  'pages': pages})


class ArticleUpdateView(View):
    # 添加权限校验
    @method_decorator(login_required)
    # 获取文章详情,得1先获取文章，要通id获取，唯一标识
    def get(self, request, id):
        # 查询到文章以后，返回文章详情页面，并且把文章对象传递给更新的前端页面
        try:
            article = Article.objects.get(id=id)
        except Article.DoesNotExist:
            return redirect('not_found')
        data = {
            'title': article.title,
            'desc': article.desc,
            'content': article.content,
            'labels': article.labels.all(),
            'classes': article.classes
        }
        # initial是表单初始化数据,修改的时候需要把原文章展示出来
        form = ArticleForm(initial=data)
        return render(request, 'demo/update_article.html',
                      context={'form': form, 'username': request.user.username})

    def post(self, request, id, **kwargs):
        # 请求获得准备发表文章的文章数据
        # 绑定用户数据表单对象
        # 当需要使用表单时并且需要绑定文件数据 文件数据需要跟普通的表单数据分开处理
        # 文件数据并不会放到POST 而是存放到file参数里面
        form = ArticleForm(request.POST, request.FILES)
        username = request.user.username
        # 用.is_valid()据验证表单数,如果验证通过，则把数据序列化出来，封装到对象里面去
        if form.is_valid():
            # 获取用户对象
            user = request.user
            # 获取表单数据,在cleaned_data对象里面获取-->cleaned_data这个是通过验证的数据封装其里面
            title = form.cleaned_data.get('title')
            desc = form.cleaned_data.get('desc')
            content = form.cleaned_data.get('content')
            cover = form.cleaned_data.get('cover')
            labels = form.cleaned_data.get('labels')
            classes = form.cleaned_data.get('classes')
            # 保存图片， 获取图片的保存路径，这里不会自动保存，要我们写保存代码。
            file_name = f'{datetime.now().strftime("%Y%m%d%H%M%S")}_{cover.name}'
            # 图片地址
            file_path = os.path.join(MEDIA_ROOT, file_name)
            # print(file_path)
            with open(file_path, 'wb') as f:
                # 当你的图片文件过大的时候 chunks 上传的文件数据分批次写入
                for chunk in cover.chunks():
                    f.write(chunk)
            # 以上的拿到页面表单里面用户修改的数据，需要封装到对象里面才能保存到数据库里。
            # 查询文章对象列表
            try:
                articles = Article.objects.filter(id=id)
            except Article.DoesNotExist:
                return redirect('not_found')
            # 先删除原来的图片
            if articles[0].cover:
                img_name = articles[0].cover.split('/')[-1]
                try:
                    os.remove(os.path.join(MEDIA_ROOT, img_name))
                except Exception as e:
                    print(e)

            # 注意：只有queryset对象才有update方法,get方法返回的是一个对象是没有update方法的
            articles.update(
                title=title,
                desc=desc,
                content=content,
                cover='/static/media/' + file_name,
                user=user,
                classes=classes)
            # 清空标签
            if len(articles[0].labels.all()):
                print(len(articles[0].labels.all()))
                articles[0].labels.clear()
            for item in labels:
                articles[0].labels.add(item)
            return redirect('article_user')


class ArticleDeleteView(View):
    # 添加权限校验
    @method_decorator(login_required)
    def get(self, request, id):
        try:
            article = Article.objects.get(id=id)
        except Article.DoesNotExist:
            return redirect('not_found')
        # 1 先删除图片
        if article.cover:
            img_name = article.cover.split('/')[-1]
            try:
                os.remove(os.path.join(MEDIA_ROOT, img_name))
            except Exception as e:
                print(e)
        # 2 删除文章
        article.delete()
        return redirect('article_user')
